\documentclass[10pt,a4paper]{scrreprt}
\usepackage{a4wide}
\usepackage[utf8x]{inputenc}
\usepackage{ucs}
\usepackage{hyperref}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage{ngerman}
\usepackage{graphicx}
\addtocounter{secnumdepth}{1} 
\usepackage{listings}
\usepackage[final]{pdfpages}
\lstset{frame=single,language=Java,breaklines=true,numbers=left}

\title{Abschlussbericht zum Projektseminar "`Nonstandard-Rendering-Methoden"'}
\author{Thomas Kluth, Alexander Richert, Sandra Mogharbel}
\date{Sommersemester 2011 \\ Prof. Clausing}

\begin{document}
\maketitle
\tableofcontents

\chapter{Aufgabenstellung}
Die Aufgabe des Projektseminars war es, ein Programm zu entwickeln, welches ein Bild als Eingabe bekommt 
und versucht dieses Bild durch Linien so zu approximieren, dass man das Originalbild wiedererkennt. Eine Linie soll sowohl ihren Start- und Endpunkt
als auch ihre Dicke verändern können. Dabei sollten genetische Algorithmen zum Einsatz kommen -- allerdings sollte für diese ein Framework verwendet werden.

\chapter{Entwurf}
Der Grundgedanke für unser Projekt war, dass ein Bild aus Strichen (ab jetzt kurz: Strichbild) eine mögliche Lösung des Problems darstellt, also ein Chromosom ist.
Ein Chromosom hat nun mehrere Gene, die in unserem Falle einen einzelnen Strich darstellen. Der erste Versuch ein eigenes Gen zu schreiben und zu benutzen, musste nach einiger Zeit fallengelassen werden. Nichtsdestotrotz ist die ursprüngliche Idee ein Gen, welches die Informationen für einen Strich zusammenfasst (Startpunkt x, Startpunkt y, Endpunkt x, Endpunkt y, Breite), zu verwenden geglückt. \\
Als Vergleichsfunktion zwischen dem Originalbild und den einzelnen Strichbildern sollte uns vorerst ein pixelweiser Vergleich genügen. \\
Als Framework für die genetischen Algorithmen haben wir uns für JGAP\footnote{http://jgap.sourceforge.net/} entschieden. Dieses Framework hat sowohl einige Standard-Operatoren
und Selektoren, ist aber auch durch Vererbung leicht erweiterbar. So lassen sich relativ einfach eigene Gene, Chromosomen und genetische Operatoren schreiben. \\
Wir haben uns für das Entwurfsmuster Model-View-Controller entschieden. Im Paket \textit{model} befinden sich alle Datenhaltungsklassen: 
\begin{itemize}
\item Die Einstellungsklasse (\texttt{Model\_Config})
\item Die Klasse, die das Originalbild verwaltet (\texttt{Model\_Image\_Original})
\item Die Klasse, die die Strichbilder -- und damit auch alle Daten für die genetischen Algorithmen -- verwaltet (\texttt{Model\_Image\_Line})
\end{itemize}
Im Paket \textit{view} befindet sich alles, was mit der graphischen Oberfläche zu tun hat. \\
Das Paket \textit{controller} enthält die eigentliche Programmlogik. Zum einen verarbeitet die Klasse \texttt{Controller} alle Aktionen, die der Nutzer auf der Programmoberfäche ausführt, zum anderen beinhaltet die Klasse \texttt{Controller\_Fitness} die Fitnessfunktion und erbt somit von der Klasse \texttt{FitnessFunction} aus dem JGAP-Framework. In der Klasse \texttt{Controller\_Fitness} läuft alles Wesentliche, was den Ablauf der genetischen Algorithmen angeht. So wird -- wie bereits genannt -- der Fitnesswert für jedes Chromosom berechnet, aber auch jedes Chromosom als Strichbild interpretiert und gezeichnet.
\begin{figure}
	\centering
		%\includepdf[scale=0.85]{klassendiagramm.pdf}
	\includegraphics[scale=0.7]{klassendiagramm.pdf}
	\caption{vereinfachtes Klassendiagramm}
	\label{klassendiagramm}
\end{figure}


\chapter{Realisierung}
\section{Genetische Algorithmen}
\subsection{Initialisierung}
Die Grundeinstellungen für die genetischen Algorithmen werden in der Klasse \texttt{Model\_Image\_Line} in der Methode \texttt{setupEvolution()} gesetzt. Hier werden zum einen JGAP-interne Einstellungen vorgenommen und zum anderen die genetischen Operatoren und Selektoren für den Evolutionsprozess festgelegt. Außerdem wird ein sogenanntes \texttt{SampleChromosome} erstellt, was als Grundlage für jedes neu erstellte Chromosom verwendet wird. Dieses SampleChromosome war in der Anfangsphase des Projekts ein \texttt{LineGen} -- ein selbst geschriebenes Gen, was alle erforderlichen Informationen eines Striches kapselte. Dieser Weg stellte sich jedoch als problematisch heraus, sodass das \texttt{LineGene} durch ein \texttt{CompositeGene} ersetzt wurde. Dabei ist ein \texttt{CompositeGene} eine von JGAP bereitgestellte Klasse, die ein aus mehreren Genen zusammengesetztes Gen implementiert. \\
Hier eine vereinfachte Version der Methode \texttt{setupEvolution}:\\
\begin{lstlisting}[caption={setupEvolution()},label=lst:setupEvolution]
TournamentSelector tournamentSelector = new TournamentSelector(genoConf, 5, 0.6d);
genoConf.addNaturalSelector(tournamentSelector, false);
genoConf.addGeneticOperator(new MutationOperator(genoConf));
FitnessFunction fitFunc = new Controller_Fitness(config, image_Original, this);
genoConf.setFitnessFunction(fitFunc);
genoConf.setPopulationSize(config.getImagesPerGeneration());
CompositeGene[] sampleGenes = new CompositeGene[config.getLinesPerImage()];
for (int i = 0; i < sampleGenes.length; i++) {
	sampleGenes[i] = new CompositeGene(genoConf);
	sampleGenes[i].addGene(new IntegerGene(genoConf, 0, getWidth()));   //x1
	sampleGenes[i].addGene(new IntegerGene(genoConf, 0, getHeight()));   //y1
	sampleGenes[i].addGene(new IntegerGene(genoConf, 0, getWidth()));   //x2
	sampleGenes[i].addGene(new IntegerGene(genoConf, 0, getHeight()));   //y2
	sampleGenes[i].addGene(new IntegerGene(genoConf, 1, 3));   //breite
	sampleGenes[i].addGene(new IntegerGene(genoConf, 0, 100)); //composite
}
Chromosome sampleChromosome = new Chromosome(genoConf, sampleGenes);
genoConf.setSampleChromosome(sampleChromosome);
genotype = Genotype.randomInitialGenotype(genoConf);
\end{lstlisting}
Wie aus dem Listing \ref{lst:setupEvolution}, Zeile 1 ersichtlich ist, haben wir als Selektion die Turnierselektion mit einer Turniergröße von 5 gewählt.
Der einzige genetische Operator ist ein Mutationsoperator (Zeile 2). Ab Zeile 7 wird das oben angesprochene \texttt{SampleChromosome} erstellt, welches aus 6 \texttt{InterGene}s besteht. Dabei haben die einzelnen Gene jeweils folgende Bedeutung (in der Reihenfolge wie sie zum \texttt{CompositeGene} hinzugefügt werden):
\begin{enumerate}
\item der x-Wert des  Startpunkts (zwischen 0 und der Breite des Bildes)
\item der y-Wert des Startpunkts (zwischen 0 und der Höhe des Bildes)
\item der x-Wert des Endpunkts(zwischen 0 und der Breite des Bildes)
\item der y-Wert des Endpunkts (zwischen 0 und der Höhe des Bildes)
\item die Breite des Striches (zwischen 1 und 3)
\item die Transparenz des Striches (zwischen 0\% (weiß) und 100\% (schwarz))
\end{enumerate}
In Zeile 19 des Listings \ref{lst:setupEvolution} schließlich wird ein zufälliger erster Genotyp erzeugt.
\subsection{Fitnessfunktion}
Die Fitnessfunktion ist in der Klasse \texttt{Controller\_Fitness} in der Methode \texttt{evaluate(IChromosome chromosome)} zu finden. Diese Methode bekommt ein Chromosom (sprich: ein Strichbild) und berechnet deren Fitnesswert, d.h. die Ähnlichkeit bzw. die Differenz zum Originalbild. Im Laufe des Projekts haben wir verschiedene Ansätze für die Fitnessfunktion gewählt, auf die wir im Folgenden eingehen werden.
\subsubsection{(variierter) Euklidischer Abstand}

\paragraph{Euklidischer Abstand}
Der naivste Ansatz ist es den Farbunterschied zwischen dem Originalbildes und dem Strichbild pixelweise zu vergleichen. Dabei wird der Abstand zweier Pixel mittels des Euklidischen Abstandes berechnet:
\[d(x,y)=\sqrt{\sum_{x=0}^{Bildbreite}\sum_{y=0}^{Bildhoehe}(original(x,y)-strich(x,y))^2} \]
Die Funktion als Java-Quelltext:

\begin{lstlisting}[caption=Euklidischer Abstand in Java-Quelltext,label=euklid1]
Chromosome chrom = (Chromosome) ic;
image_line.setActualImage(createLineImage(chrom));
int[][] lineArray = image_line.getGrayValues();
int[][] originalArray = image_original.getGrayValues();
double diff = 0, fit;
for (int x = 0; x < originalArray.length; x++) {
	for (int y = 0; y < originalArray[0].length; y++) {
		diff += Math.pow(originalArray[x][y] - lineArray[x][y], 2);
	}
}
fit = Math.sqrt(diff);
return fit;
\end{lstlisting}
Diese naive Methode funktioniert zuverlässig, hat aber den Nachteil, dass sie sehr rechenintensiv ist und deshalb entsprechend viel Zeit benötigt. Aufgrunddessen haben wir nach einer Variation dieses Ansatzes gesucht, um eine schnellere Konvergenz des Strichbildes zu erreichen.

\paragraph{Verringerte Anzahl von Vergleichen}
Die erste Idee für eine Variation des Euklidischen Abstandes war es, die Anzahl der Vergleiche zu reduzieren, indem man nicht mehr jedes Pixel des Originalbildes mit jedem Pixel des Strichbildes vergleicht, sondern beispielsweise nur noch jedes zweite (oder sogar noch weniger) Pixel in den Vergleich miteinbezieht. In der Praxis haben wir den Code in Listing \ref{euklid1} wie folgt verändert:
\begin{lstlisting}[caption=Euklidischer Abstand mit weniger Vergleichen,label=euklid2]
Chromosome chrom = (Chromosome) ic;
image_line.setActualImage(createLineImage(chrom));
int[][] lineArray = image_line.getGrayValues();
int[][] originalArray = image_original.getGrayValues();
double diff = 0, fit;
int sprung = 2;
x = (x + 1) % sprung;
y = (y + 1) % sprung;
for (; x < (originalArray.length - 1); x = x + sprung) {
	for (; y < (originalArray[0].length - 1); y = y + sprung) {
		diff += Math.pow(originalArray[x][y] - lineArray[x][y], 2);
	}
}
fit = Math.sqrt(diff) * sprung * 2;
return fit;
\end{lstlisting}
In Zeile 6 von Listing \ref{euklid2} wird eine zusätzliche Variable eingeführt, die bestimmt, welches das nächste zu betrachtende Pixel ist. Bei \texttt{sprung = 1} handelt es sich um die unveränderte Version des Euklidischen Abstands, bei \texttt{sprung = 2} wird nur noch ein Viertel aller Pixel (die Hälfte aller Pixel pro Breite und die Hälfte aller Pixel pro Höhe des Bildes) betrachtet. D.h. je größer \texttt{sprung}, desto weniger Pixel werden betrachtet. Die Zeilen 7 und 8 sorgen dafür, dass der Vergleich nicht immer mit dem gleichen Pixel beginnt, sondern dass im Laufe der Zeit (spätestens nach \texttt{sprung} Aufrufen der Methode) alle Pixel betrachtet werden.\\
Leider führte diese Methode weder zum erhofften Zeitgewinn (es ließ sich lediglich eine kleine Geschwindigkeitserhöhung feststellen), noch wurden die Ergebnisse besser -- im Gegenteil der beobachtete minimale Zeitgewinn wurde durch eine "`Unschärfe"' des Strichbildes zunichte gemacht. Also probierten wir ab einer gewissen Anzahl von Generationen wieder alle Pixel miteinander zu vergleichen -- diese Praxis scheiterte leider oft (wenn auch nicht immer) daran, dass sich der Fitnesswert und damit auch das Strichbild überhaupt nicht mehr veränderten. Die Erklärung liegt wohl darin, dass sich auf einmal ein relativ stark nach oben abweichender Fitnesswert ergab (trotz Multiplikation des Fitnesswertes mit \texttt{sprung}). Bei dieser starken Änderung des Fitnesswertes -- in die "`falsche"' Richtung -- versagte JGAP.

\paragraph{Verkleinertes Originalbild}
Der nächste Versuch, die Fitnessfunktion schneller konvergieren zu lassen, kam von Prof. Clausing bei einem Vorstellen der Zwischenergebnisse: Anstatt mit der kompletten Größe des Originalbildes zu rechnen, war die Idee, ein kleineres Bild zu verwenden. Die Größe des Strichbildes allerdings bei der originalen Größe zu belassen.\\
Dies brachte einen erheblichen Geschwindigkeitszuwachs, scheiterte aber an ähnlichen Problemen wie die vorhin diskutierte Variante: Ab einer gewissen Güte des Strichbildes, wird zwar der Fitnesswert immer besser, das Bild selber aber scheint sehr unscharf. Was sich aus dem Experimentieren mit dieser Idee allerdings ergab ist, dass ein Bild mit 200x150 Pixel wesentlich schneller und erfolgreicher konvergiert als das selbe Bild mit einer Auflösung von 400x300 Pixeln. Deshalb werden in der finalen Version alle Bilder automatisch so skaliert, dass sie maximal 200 Pixel in der Breite bzw. Höhe haben (es sei denn, sie sind bereits noch kleiner). Zusätzlich werden alle Eingabebilder in ein Graustufenbild
 umgerechnet.

\paragraph{Gewichtete Bereiche}
Eine weitere von Prof. Clausing eingebrachte Idee war es, vor Beginn der Evolution im Originalbild bestimmte Bereiche per Mausklick zu gewichten. Dies haben wir realisiert, indem wir ein \texttt{BufferedImage} als Maske über das Originalbild gelegt haben. Auf dieser Maske kann der Nutzer die gewünschten, stärker zu gewichtenden Bereiche markieren. Bei der Berechnung des Fitnesswertes wird dann auf diese Maske geschaut, um dieses Pixel des Bildes eventuell stärker zu gewichten.
\begin{lstlisting}[caption=Euklidischer Abstand mit gewichteten Bereichen,label=euklid3]
Chromosome chrom = (Chromosome) ic;
image_line.setActualImage(createLineImage(chrom));
lineArray = image_line.getGrayValues();
double diff = 0, fit;
double weight = 1D;
originalArray = image_original.getGrayValues();
maskArray = image_original.getGrayValuesMaske();

for (int x=0; x < originalArray.length; x++) {
	for (int y=0; y < originalArray[0].length; y++) {
        	if (maskArray[x][y] > 200) { //Maske ist weiss -> starke Gewichtung
                	weight = 2D;
		} else if (maskArray[x][y] > 100) { //Maske ist grau -> einfache Gewichtung
			weight = 1.5D;
		}
		diff += Math.pow(originalArray[x][y] - lineArray[x][y], 2) * weight;
	}
}
fit = Math.sqrt(diff); 
return fit;
\end{lstlisting}

Es gibt 3 Gewichtungsstufen:
\begin{enumerate}
 \item keine Gewichtung (entspicht dem normalen Wert)
 \item einfache Gewichtung (entspricht dem 1,5-fachen des normalen Werts)
 \item starke Gewichtung (entspricht dem 2-fachen des normalen Werts)
\end{enumerate}
Die oben genannten Werte haben sich experimentell als gut für eine erste Annäherung erwiesen. Höhere Werte von \texttt{weight} (etwa: starke Gewichtung: 3-facher Wert, mittlere Gewichtung: 2-facher Wert) scheinen vor allem bei längeren Evolutionen bessere Ergebnisse zu liefern.

\subsubsection{Fitnesswertberechnung ausgehend von der Lage der einzelnen Striche}
Eine ganz andere Heransgehenweise war es, den Fitnesswert nicht anhand eines kompletten Strichbildes zu berechnen, sondern für jeden einzelnen Strich einen Wert zu berechnen und diese dann über alle Striche zu summieren. Dazu haben wir jeden Strich als Gerade aufgefasst und durch eine Geradengleichung die Punkte des Striches mit den entsprechenden Pixeln des Originalbildes verglichen. Dies erspart das sehr zeitaufwändige Erstellen eines \texttt{BufferedImage} aus jedem Chromosom und versprach deshalb einen hohen Geschwindigkeitszuwachs. In Listing \ref{geradengleichung} ist ein vereinfachter Quelltextausschnitt der entsprechenden \texttt{evaluate()}- Methode.\\

\begin{lstlisting}[caption=einzelne Striche als Geradengleichung interpretiert,label=geradengleichung]
originalArray = image_original.getGrayValues();
Chromosome chrom = (Chromosome) ic;
CompositeGene gen;
double fit = 0, diff, steigung, b, composite, temp;
int x1, y1, x2, y2, swap, stroke, lineColor;
int[] nextPoint = new int[2];

for (int i = 0; i < chrom.getGenes().length; i++) {
	gen = (CompositeGene) chrom.getGene(i);
	x1 = (Integer) gen.geneAt(0).getAllele();
	y1 = (Integer) gen.geneAt(1).getAllele();
	x2 = (Integer) gen.geneAt(2).getAllele();
	y2 = (Integer) gen.geneAt(3).getAllele();
	stroke = (Integer) gen.geneAt(4).getAllele();
	composite = (((Integer) gen.geneAt(5).getAllele()) / 100d);

	steigung = (double) (y2 - y1) / (x2 - x1);
	b = (int) (y1 - steigung * x1);
	diff = 0;
	lineColor = (int) (255 * (1 - composite)); //Transparanz des Striches beruecksichtigen 
	
	while (x1 <= x2) {
		nextPoint[1] = (int) ((steigung * x1) + b); //naechster y-Wert
    		nextPoint[0] = x1++;	//naechster x-Wert
    		for (int s = 0; s < stroke; s++) { //Breite beruecksichtigen
    			temp = Math.abs(lineColor - originalArray[Math.abs(nextPoint[0] + s)][Math.abs(nextPoint[1] + s)]);
    			diff += temp;
    		}
    	}
	fit += diff;
}
return fit;
\end{lstlisting}
Dieser Ansatz brachte zwar den erhofften Geschwindigkeitszuwachs, leider jedoch nicht die erhoffte verbesserte Konvergenz. Zum einen liegt dies daran, dass (fast) senkrechte Striche so gut wie garnicht in die Berechnung eingehen, zum anderen daran, dass keine Berücksichtigung der Wechselwirkung mit den anderen Strichen gemacht wurde. Der Versuch dieses zweite Problem zu beheben, würde letzten Endes wohl noch mehr Vergleiche haben, als die oben beschriebenen Varianten des Euklidischen Abstandes, da für jeden Strich jeder Schnittpunkt mit anderen Strichen berechnet werden müsste.

\chapter{Bedienung des Programms}
\section{Steuerung des Programms}
\paragraph{Menüleiste}Die Bedienelemente zur Steuerung des Programms befinden sich in der linken oberen Ecke. Durch den Button \includegraphics[scale=0.9]{scanner.png} kann ein Bild in das Programm geladen werden. Das Bild wird automatisch zu einem Graustufenbild konvertiert und skaliert. \\
Nun kann man den Evolutionspozess mithilfe des Buttons \includegraphics[scale=0.6]{start.png} starten und jederzeit mit ebendiesen Button (nun \includegraphics[scale=0.6]{pause.png}) wieder pausieren. \\
Der Button \includegraphics[scale=0.6]{bildSpeichern.png} speichert das momentan fitteste Bild (also das was auf der rechten Seite des Programms angezeigt wird) als svg-Bild.\\ 
Der Button \includegraphics[scale=0.6]{prozessSpeichern.png} speichert den gesamten Evolutionsprozess, den man zu einem späteren Zeitpunkt mithilfe des Buttons \includegraphics[scale=0.6]{prozessLaden.png} wieder laden und dann fortsetzen kann.
\paragraph{Gewichtung und Kontrast}
Führt man einen Doppelklick auf das Originalbild aus, öffnet sich ein Fenster, indem man eine Gewichtung des Originalbildes vornehmen kann. In diesem Fenster lassen sich die Pinselgröße und die Stärke der Gewichtung (keine, normal, stark) einstellen. Mithilfe der Maus kann man nun die Bereiche in dem Bild, deren Ähnlickeit wichtiger als andere Bereiche des Bildes sind, markieren.
Mit der Wahl "`Kontrast"' lassen sich Bereiche des Bildes aufhellen oder verdunkeln.

\section{Einstellungen}
Unter dem Reiter Einstellungen befinden sich diverse Einstellungen zum Programmverhalten. \\
Dies fängt an mit zwei Einstellungen, die die genetischen Algorithmen betreffen. So lassen sich die Anzahl der Striche pro Bild und die Anzahl der Bilder pro Generation einstellen. Dies kann jedoch nur vor dem Start des Evolutionsprozesses stattfinden! \\
Wenn man auf das Strichbild mit der linken Maustaste klickt, erscheint das Originalbild im Hintergrund. Ein weiterer Linksklick blendet es wieder aus. Wie stark das Originalbild zu sehen ist, lässt sich mit der Einstellung "`Transparenz"' einstellen. Dabei bedeutet 0, dass das Strichbild nicht mehr zu sehen ist, und 100, dass das Originalbild nicht mehr zu sehen ist. \\
Ist die Einstellung "`Bilder-Log"' aktiviert, wird regelmäßig das fitteste Bild als Zwischenergebnis in einem Verzeichnis abgelegt. Dabei kann man das Interval, in welchem das Bild gespeichert werden soll (jedes 1.000ste Bild, jedes 100ste Bild oder ähnliches) einstellen. In dem Textfeld unter dieser Einstellung lässt sich der Ordner, in dem die Bilder gespeichert werden benennen -- zur genaueren Unterscheidung wird automatisch das aktuelle Datum und die aktuelle Uhrzeit hinzugefügt. \\
Die Einstellung "`Skaliermethode"' bezieht sich auf die Art und Weise wie das Strichbild skaliert wird (wenn es überhaupt skaliert wird).\\
Die Einstellung "`Tooltip"' schaltet Tooltips ein bzw. aus, "`Verzögerung"' stellt die Zeit ein, die vergeht bis die Tooltips erscheinen.\\
Ist die Einstellung "`Echtzeit"' aktiviert, kann man die Veränderung der einzelnen Programmfächeninhalte beim Verkleinern/Vergrößern der Programmflächen in Echtzeit sehen. 

\chapter{Ergebnisse}\label{ergebnisse}
\centering
\begin{tabular}{ c  c  c }
	\includegraphics[width=75px, height=100px]{bilder/1954-turingklein.jpg} & \includegraphics[width=75px, height=100px]{bilder/turing4stunden500striche.png} &  
	\begin{tabular}{l} Striche pro Bild: 500 \\ Bilder pro Generation: 15\\ Generation: 40.000\newline\\\newline\\\newline\\\newline\\\newline\\\newline\\\newline\\\newline\end{tabular} \\
	\includegraphics[width=75px, height=100px]{bilder/katze.png} & \includegraphics[width=75px, height=100px]{bilder/katze75000.png} &  
	\begin{tabular}{l} Striche pro Bild: 800 \\ Bilder pro Generation: 15\\ Generation: 75.000\\\newline\\\newline\\\newline\\\newline\\\newline\\\newline\\\newline\\\newline\end{tabular}\\
\end{tabular}\\
\centering
850 Striche, 15 Bilder pro Generation\\
\begin{tabular}{ c c c c c c }
\includegraphics[width=40px, height=40px]{bilder/key/key.png} & \includegraphics[width=40px, height=40px]{bilder/key/7000.png} & \includegraphics[width=40px, height=40px]{bilder/key/10000.png} & \includegraphics[width=40px, height=40px]{bilder/key/20000.png} & \includegraphics[width=40px, height=40px]{bilder/key/22000.png} & \includegraphics[width=40px, height=40px]{bilder/key/27000.png} \\
Original & 7.000, & 10.000, & 20.000, & 22.000, & 27.000,\\ 
\includegraphics[width=40px, height=40px]{bilder/key/36000.png} & \includegraphics[width=40px, height=40px]{bilder/key/40000.png} & \includegraphics[width=40px, height=40px]{bilder/key/77000.png} & \includegraphics[width=40px, height=40px]{bilder/key/110000.png} & \includegraphics[width=40px, height=40px]{bilder/key/150000.png} & \includegraphics[width=40px, height=40px]{bilder/key/300000.png}\\
 36.000, & 40.000, & 77.000, & 110.000, & 150.000, & 300.000\\
\end{tabular}\\ 

\begin{tabular}{ccc}
	& \includegraphics[width=86px, height=100px]{bilder/marilyn/marilyn.png} &  \\
	& \begin{tabular}{l} Striche pro Bild: 850 \\ Bilder pro Generation: 15 \end{tabular} & \\
	\includegraphics[width=86px, height=100px]{bilder/marilyn/10000.png} &  \includegraphics[width=86px, height=100px]{bilder/marilyn/50000.png} &\includegraphics[width=86px, 																						height=100px]{bilder/marilyn/200000.png} \\
	Generation 10.000  & Generation 50.000 & Generation 200.000\\ \newline
	
\end{tabular}
\newline

\begin{tabular}{ c  c  c }
	& \includegraphics[width=100px, height=80px]{bilder/gauss/gauss.png} &  \\
	& \begin{tabular}{l} Striche pro Bild: 850 \\ Bilder pro Generation: 15 \end{tabular}  & \\

	\includegraphics[width=100px, height=80px]{bilder/gauss/2000.png} &  \includegraphics[width=100px, height=80px]{bilder/gauss/5000.png} &\includegraphics[width=100px, 																						height=80px]{bilder/gauss/10000.png} \\
	Generation 2.000 &Generation 5.000 & Generation 10.000\\
	\includegraphics[width=100px, height=80px]{bilder/gauss/20000.png} & \includegraphics[width=100px, height=80px]{bilder/gauss/100000.png} &\includegraphics[width=100px, height=80px]{bilder/gauss/200000.png}\\
	Generation 20.000 & Generation 100.000 & Generation 200.000\\ \newline
\end{tabular}

\chapter{Fazit}
\begin{flushleft}
Im Verlauf des Seminars erkannten wir, dass keine optimalen Parameter für die Berechnungen existieren.
Um detailreiche Bilder zu generieren muss die Strichanzahl stark erhöht werden; dies führt zwar zur erhofften Feinheit geht jedoch einher mit wesentlich höherer Rechenzeit. Dabei kann der Prozess endlos fortgeführt werden, jedoch verbessert sich das Bild im Laufe der Generationen deutlich langsamer, wie das folgende Diagramm veranschaulicht. 
\end{flushleft}

\includegraphics[width=420px]{diagramm.png}



\end{document}